/* 
Scenic Graphics Library
Copyright (C) 2007 Jouni Tulkki

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USApackage scenic;
*/

#include <jni.h>
#include "scenic_ScenicImage.h"

#include "common.h"
#include "Debug.h"


using namespace Scenic::Renderer;


JNIEXPORT jint JNICALL Java_scenic_ScenicImage_nativeCreate
  (JNIEnv *, jobject, jint width, jint height, jint format, jint options)
{
	Synchronized sync(renderingLock);

	ENTER(Java_scenic_Image_nativeCreate);

	Image *image = new Image();

	if(!image->create(width, height, (Format)format, options))
	{
		delete image;
		
		return 0;
	}
	
	return image->id;
}

JNIEXPORT void JNICALL Java_scenic_ScenicImage_nativeFree
  (JNIEnv *, jobject, jint id)
{
	Synchronized sync(renderingLock);

	ENTER(Java_scenic_Image_nativeFree);

	Image *image = Image::get(id);

	if(!image)
	{	
		return;
	}
	image->free(true);
	delete image;
}

JNIEXPORT jboolean JNICALL Java_scenic_ScenicImage_nativeContentsLost
  (JNIEnv *, jobject, jint id)
{
	Synchronized sync(renderingLock);

	ENTER(Java_scenic_Image_nativeContentsLost);

	Image *image = Image::get(id);

	if(!image)
	{
		return false;
	}

	return image->contentsLost();
}

static bool isInsideBounds(Scenic::Renderer::Format format, int width, int height, int offset, int pitch, int length)
{
	int pixelSize = Device::getFormatSize(format);

	if(pitch == 0)
	{
		pitch = width * pixelSize;
	}

	int size = (height - 1) * pitch + width * pixelSize;

	return offset >= 0 && size + offset <= length;
}

static void throwBoundsException(JNIEnv *env)
{
	jclass clazz = env->FindClass("java/lang/ArrayIndexOutOfBoundsException");

	env->ThrowNew(clazz, "The given array is too small.");
}

JNIEXPORT void JNICALL Java_scenic_ScenicImage_nativeWriteByte
  (JNIEnv *env, jobject, jint id, jint x, jint y, jint width, jint height, jbyteArray data, 
  jint offset, jint pitch, jint format)
{
	Synchronized sync(renderingLock);

	ENTER(Java_scenic_Image_nativeWriteByte);

	Image *image = Image::get(id);

	if(!image)
	{		
		return;
	}
	if(!isInsideBounds((Format)format, width, height, offset, pitch, env->GetArrayLength(data)))
	{
		throwBoundsException(env);
		return;
	}

	jbyte *dataBytes = env->GetByteArrayElements(data, NULL);

	image->write(x, y, width, height, (char*)dataBytes + offset, pitch, (Format)format);
	env->ReleaseByteArrayElements(data, dataBytes, JNI_ABORT);
}

JNIEXPORT void JNICALL Java_scenic_ScenicImage_nativeWriteInt
  (JNIEnv *env, jobject, jint id, jint x, jint y, jint width, jint height, jintArray data, 
  jint offset, jint pitch, jint format)
{
	Synchronized sync(renderingLock);

	ENTER(Java_scenic_Image_nativeWriteInt);

	Image *image = Image::get(id);

	if(!image)
	{		
		return;
	}
	if(!isInsideBounds((Format)format, width, height, offset, pitch, env->GetArrayLength(data) * 4))
	{
		throwBoundsException(env);
		return;
	}

	jint *dataBytes = env->GetIntArrayElements(data, NULL);

	image->write(x, y, width, height, (char*)dataBytes + offset * 4, pitch * 4, (Format)format);
	env->ReleaseIntArrayElements(data, dataBytes, JNI_ABORT);
}

JNIEXPORT void JNICALL Java_scenic_ScenicImage_nativeWriteFloat
  (JNIEnv *env, jobject, jint id, jint x, jint y, jint width, jint height, jfloatArray data, 
  jint offset, jint pitch, jint format)
{
	Synchronized sync(renderingLock);

	ENTER(Java_scenic_Image_nativeWriteFloat);

	Image *image = Image::get(id);

	if(!image)
	{		
		return;
	}
	if(!isInsideBounds((Format)format, width, height, offset, pitch, env->GetArrayLength(data) * 4))
	{
		throwBoundsException(env);
		return;
	}

	jfloat *dataBytes = env->GetFloatArrayElements(data, NULL);

	image->write(x, y, width, height, (char*)dataBytes + offset * 4, pitch * 4, (Format)format);
	env->ReleaseFloatArrayElements(data, dataBytes, JNI_ABORT);
}

JNIEXPORT void JNICALL Java_scenic_ScenicImage_nativeReadByte
  (JNIEnv *env, jobject, jint id, jint x, jint y, jint width, jint height, jbyteArray data, 
  jint offset, jint pitch, jint format)
{
	Synchronized sync(renderingLock);

	ENTER(Java_scenic_Image_nativeReadByte);

	Image *image = Image::get(id);

	if(!image)
	{
		return;
	}
	if(!isInsideBounds((Format)format, width, height, offset, pitch, env->GetArrayLength(data)))
	{
		throwBoundsException(env);
		return;
	}

	jbyte *dataBytes = env->GetByteArrayElements(data, NULL);

	image->read(x, y, width, height, (char*)dataBytes + offset, pitch, (Format)format);
	env->ReleaseByteArrayElements(data, dataBytes, 0);
}

JNIEXPORT void JNICALL Java_scenic_ScenicImage_nativeReadInt
  (JNIEnv *env, jobject, jint id, jint x, jint y, jint width, jint height, jintArray data, 
  jint offset, jint pitch, jint format)
{
	Synchronized sync(renderingLock);

	ENTER(Java_scenic_Image_nativeReadInt);

	Image *image = Image::get(id);

	if(!image)
	{
		return;
	}
	if(!isInsideBounds((Format)format, width, height, offset, pitch, env->GetArrayLength(data) * 4))
	{
		throwBoundsException(env);
		return;
	}

	jint *dataBytes = env->GetIntArrayElements(data, NULL);

	image->read(x, y, width, height, (char*)dataBytes + offset * 4, pitch * 4, (Format)format);
	env->ReleaseIntArrayElements(data, dataBytes, 0);
}

JNIEXPORT void JNICALL Java_scenic_ScenicImage_nativeReadFloat
  (JNIEnv *env, jobject, jint id, jint x, jint y, jint width, jint height, jfloatArray data, 
  jint offset, jint pitch, jint format)
{
	Synchronized sync(renderingLock);

	ENTER(Java_scenic_Image_nativeReadFloat);

	Image *image = Image::get(id);

	if(!image)
	{
		return;
	}
	if(!isInsideBounds((Format)format, width, height, offset, pitch, env->GetArrayLength(data) * 4))
	{
		throwBoundsException(env);
		return;
	}

	jfloat *dataBytes = env->GetFloatArrayElements(data, NULL);

	image->read(x, y, width, height, (char*)dataBytes + offset * 4, pitch * 4, (Format)format);
	env->ReleaseFloatArrayElements(data, dataBytes, 0);
}
